/**
 * 
 */
package algorithm;

import info.Constant;
import info.Course;
import info.Room;
import infomanager.GenerationManager;
import io.Output;
import evolution.Gene;
import evolution.Generation;
import evolution.Individual;

/**
 * @author vtkhanh
 * 
 */
public class Evolutionary {        
    private Course[] mCourses;
    private Room[] mRooms;
    // used to keep choosen-parent from duplicating
//    private ArrayList<Individual> mStore = new ArrayList<Individual>();

    public Evolutionary(Course[] courses, Room[] rooms) {
	mCourses = courses;
	mRooms = rooms; 
    }

  
    public Individual solve() {
	Generation generation = initFirstGeneration();
	int min = Integer.MAX_VALUE;
	Individual choose = null;
	Individual tmp;

	for (int i = 0; i < Constant.GENERATION_COUNT; i++) {
	    System.out.println("Lap : " + i);
	    
	    Generation nextGeneration = evaluateThenModify(generation);

	    GenerationManager.evaluate(nextGeneration, null, false);

	    generation = chooseNextGeneration(nextGeneration);

	    tmp = generation.getBestIndividual();
	    if (tmp == null) {
		break;
	    }
	    else if (tmp.getCost() < min) {
		min = tmp.getCost();
		Output.print(tmp);
		choose = new Individual(tmp);
	    }
	}

	return choose;
    }

    private Generation chooseNextGeneration(Generation generation) {
	Generation result = new Generation();
	Individual chosen = null;
	int minCost;

	for (short i = 0; i < Constant.PARENT_COUNT; i++) {
	    if (generation.getSize() == 0) {
		break;
	    }
	    minCost = Integer.MAX_VALUE;
	    for (Individual individual : generation.getIndividuals()) {
		if (individual.getCost() < minCost) {
		    chosen = individual;
		    minCost = individual.getCost();
		}
	    }
	    result.addIndividual(chosen);
	    generation.removeIndividual(chosen);
	}

	return result;
    }

/*    private boolean notAppearInStore(Individual individual) {
	// TODO Auto-generated method stub
	for (Individual element : mStore) {
	    if (element.isSame(individual)) {
		return false;
	    }
	}
	return true;
    }*/

    private Generation evaluateThenModify(Generation generation) {
	Generation nextGeneration = new Generation();
	// evaluate and then modify
	GenerationManager.evaluate(generation, nextGeneration, true);
	return nextGeneration;
    }

    private Generation initFirstGeneration() {
	int courseCount = mCourses.length;
	Individual individual = new Individual();
	boolean nextGene = false;

	for (int i = 0; i < courseCount; i++) {
	    for (short timeslot = 0; timeslot < Constant.TIMESLOT_NUM; timeslot++) {
		for (int j = 0; j < mRooms.length; j++) {
		    if (mCourses[i].isFreeAtTimeslot(timeslot)
			    && mRooms[j].isFreeAtTimeslot(timeslot)) {
			Gene gene = new Gene(i, mCourses[i],
				mRooms[j].getRoomId(), timeslot);

			// register for room[j] on timeslot
			mRooms[j].fillTimeslot(timeslot);

			// add new gene to individual
			individual.addGene(gene);

			nextGene = true;
			break;
		    }
		}
		if (nextGene) {
		    nextGene = false;
		    break;
		}
	    }
	}

	Generation generation = new Generation();
	generation.addIndividual(individual);

	// test
	Output.print(individual);

	return generation;
    }
}
